package com.simplefan.framework.common.rest;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

public class RestResultAnalyzer {

  public RestResultAnalyzer(){

  }

  public RestResultAnalyzer(String xmlFragment){
    this.xmlFragment = xmlFragment;

    if(xmlFragment != null) {
      SAXReader reader = new SAXReader();
      InputStream in = new ByteArrayInputStream(xmlFragment.getBytes());
      try {
        Document document = reader.read(in);
        Element root = document.getRootElement();
        Element element;
        Iterator i = root.elementIterator();
        while(i.hasNext()) {
          element = (Element)i.next();
          if(element.getName().equals("successful")) {
            String value = element.getStringValue();
            if(value != null && !value.equals("")) {
              isSuccessful = Boolean.parseBoolean(value);
            }
          }
          if(element.getName().equals("message")) {
            errorMessage = element.getStringValue();
          }
          if(element.getName().equals("exception")) {
            exceptionStack = element.getStringValue();
          }
          if(element.getName().equals("result")) {
            restInnerParameter = new RestInnerParameter();
            String typeName = element.attributeValue("type");
            String innerTypeName = element.attributeValue("itemType");
            String value = element.getStringValue();
            if(typeName != null && typeName.equals("list")) {
              restInnerParameter = new RestInnerListParameter();
              ((RestInnerListParameter)restInnerParameter).setItemType(innerTypeName);
              RestListAnalyzer restListAnalyzer = RestObjectAnalyzerFactory.getListAnalyzer();
              if(restListAnalyzer == null) {
                throw new RestAnalyzerException("Can not find an analyzer for parameter type list");
              }
              restInnerParameter.setValue(restListAnalyzer.toValue(value, innerTypeName));
            }
            else {
              RestObjectAnalyzer analyzer = RestObjectAnalyzerFactory.getAnalyzer(typeName);
              if(analyzer == null) {
                throw new RestAnalyzerException("Can not find an analyzer for parameter type " + typeName);
              }
              restInnerParameter.setValue(analyzer.toValue(value));
            }

            restInnerParameter.setTypeName(typeName);
          }
        }
      }
      catch(DocumentException error) {
        throw new RestAnalyzerException(error);
      }
      finally {
        try {
          in.close();
        }
        catch(IOException error) {
          throw new RestAnalyzerException(error);
        }
      }
    }
  }

  public String toXmlFragment() {
    StringBuffer buffer = new StringBuffer();
    buffer.append("<response>");
    printSuccessful(buffer);
    printMessage(buffer);
    printException(buffer);

    printResult(buffer);

    buffer.append("</response>");
    xmlFragment = buffer.toString();

    return xmlFragment;
  }

  @SuppressWarnings("unchecked")
  private void printResult(StringBuffer buffer) {
    if(restInnerParameter != null) {
      buffer.append("<result ");
      buffer.append("type=\"");
      buffer.append(restInnerParameter.getTypeName());
      buffer.append("\" ");
      if(restInnerParameter.getTypeName() != null && restInnerParameter.getTypeName().equals("list")) {
        RestListAnalyzer restListAnalyzer = RestObjectAnalyzerFactory.getListAnalyzer();
        if(restListAnalyzer == null) {
          throw new RestAnalyzerException("Can not find an analyzer for type " + restInnerParameter.getTypeName());
        }

        String itemTypeName = ((RestInnerListParameter)restInnerParameter).getItemType();
        if(itemTypeName == null) {
          for(Object object : (List)restInnerParameter.getValue()) {
            itemTypeName = RestObjectAnalyzerFactory.getTypeNameByClassName(object.getClass().getName());
            break;
          }
        }

        buffer.append("itemType=\"");
        buffer.append(itemTypeName);
        buffer.append("\" ");
        buffer.append(">");
        if(itemTypeName != null) {
          buffer.append(restListAnalyzer.toXmlFragment((List)restInnerParameter.getValue(), itemTypeName));
        }
        else {
          buffer.append("null");
        }
      }
      else {
        RestObjectAnalyzer analyzer = RestObjectAnalyzerFactory.getAnalyzer(restInnerParameter.getTypeName());
        if(analyzer == null) {
          throw new RestAnalyzerException("Can not find an analyzer for type " + restInnerParameter.getTypeName());
        }
        buffer.append(">");
        buffer.append(analyzer.toXmlFragment(restInnerParameter.getValue()));
      }

      buffer.append("</result>");
    }
  }

  private void printException(StringBuffer buffer) {
    buffer.append("<exception>");
    buffer.append(exceptionStack);
    buffer.append("</exception>");
  }

  private void printMessage(StringBuffer buffer) {
    buffer.append("<message>");
    buffer.append(errorMessage);
    buffer.append("</message>");
  }

  private void printSuccessful(StringBuffer buffer) {
    buffer.append("<successful>");
    buffer.append(isSuccessful);
    buffer.append("</successful>");
  }

  public String getErrorMessage() {
    return errorMessage;
  }

  public void setErrorMessage(String errorMessage) {
    this.errorMessage = errorMessage;
  }

  public String getExceptionStack() {
    return exceptionStack;
  }

  public void setExceptionStack(String exceptionStack) {
    this.exceptionStack = exceptionStack;
  }

  public boolean isSuccessful() {
    return isSuccessful;
  }

  public void setSuccessful(boolean isSuccessful) {
    this.isSuccessful = isSuccessful;
  }

  public RestInnerParameter getRestInnerParameter() {
    return restInnerParameter;
  }

  public void setRestInnerParameter(RestInnerParameter restInnerParameter) {
    this.restInnerParameter = restInnerParameter;
  }

  private String errorMessage;

  private String exceptionStack;

  private boolean isSuccessful = true;

  private RestInnerParameter restInnerParameter;

  private String xmlFragment;
}
